Skip to content

Conversation

@idid10
Copy link
Contributor

@idid10 idid10 commented Jan 4, 2026

🔥 작업 내용

  • 온보딩 페이지 UI
  • 프로필 이미지 업로드

🤔 추후 작업 사항

📸 작업 내역 스크린샷

image image image

🔗 이슈

  • 링크

Summary by CodeRabbit

  • 새로운 기능

    • 다단계 온보딩 흐름 및 온보딩 프로필 페이지 추가
    • 자동 폭 조절 이름 입력, 프로필 이미지 픽커·업로드 시트, 진행 점(ProgressDots) 컴포넌트 추가
    • 프로필 이미지 업로드 훅 추가
  • 개선 사항

    • 로딩 모달에 스피너와 배경 옵션 추가 및 표시 개선
    • 회원가입 마지막 화면 네비게이션을 온보딩으로 변경
    • 버튼 레이아웃·타이포그래피 및 몇몇 화면 레이아웃 조정
    • 비밀번호 검증을 최소 길이(8자)로 단순화
  • 기타

    • 동의 항목·온보딩 관련 상수·타입 추가, 글로벌 스타일에 로더 애니메이션 추가 및 일부 그라디언트 제거
    • 불필요 의존성 제거 및 소규모 텍스트 일부 수정 ("홈화면" → "홈 화면")

✏️ Tip: You can customize this high-level summary in your review settings.

@idid10 idid10 linked an issue Jan 4, 2026 that may be closed by this pull request
5 tasks
@coderabbitai
Copy link

coderabbitai bot commented Jan 4, 2026

Walkthrough

온보딩 흐름 관련 페이지·컴포넌트·상수·훅과 프로필 업로드 훅 및 로더 모달이 추가·수정되었고, 홈 컴포넌트명 변경 및 일부 CSS 변수 및 package 의존성이 제거되었습니다.

Changes

Cohort / File(s) Summary
온보딩 페이지·라우팅
src/app/onboarding/page.tsx, src/app/onboarding/profile/page.tsx, src/app/page.tsx, src/app/signup/password/page.tsx
온보딩 페이지·프로필 페이지 추가, 홈 컴포넌트명 HomeHomePage로 변경, 회원가입 완료 라우팅 대상 /login/onboarding으로 변경
온보딩 컴포넌트
src/components/onboarding/OnboardingStep.tsx, src/components/onboarding/OnboardingProfile.tsx, src/components/onboarding/ProgressDots.tsx, src/components/onboarding/ProfileImagePicker.tsx, src/components/onboarding/UploadButton.tsx
다단계 온보딩 UI 및 프로필 설정 관련 신규 클라이언트 컴포넌트 추가(단계, 프로필 픽커, 업로드 시트, 진행 점 등)
공통 컴포넌트·모달·입력
src/components/common/NameInput.tsx, src/components/common/LoadingModal.tsx, src/components/common/FullButton.tsx
동적 너비 입력 컴포넌트 추가, AlertModalLoadingModal로 API 변경(백드롭 옵션·isLoading·message optional), FullButton 내부 레이아웃 스타일 변경
훅·유틸
src/hooks/useProfileImageUpload.ts, src/hooks/usePasswordValidation.ts, src/utils/upload.ts
프로필 이미지 업로드 훅 추가(객체 URL 관리, 업로드/리셋/취소), 비밀번호 검증 로직 단순화(최소 길이), 업로드 유틸은 주석 처리됨
상수·타입
src/constants/onboardingSteps.ts, src/constants/agreement.ts, src/constants/alert.ts, src/types/agreement.type.ts
온보딩 스텝·콘텐츠 상수, 동의 항목 및 초기 상태, AlertBackdrop 타입·클래스 매핑, AgreementKey 타입 추가
스타일·설정
src/styles/globals.css, package.json
.loader 스피너 및 keyframes 추가, --color-dim-02·.bg-dim02-gradient 제거, package.json에서 @toss/use-funnel 의존성 제거
기타 UI 조정
src/components/dailyRecord/MissionFeed.tsx, src/components/signup/ProfileForm.tsx, src/components/password/PasswordForm.tsx, 여러 src/app/* 페이지들
레이아웃/클래스 조정 및 AlertModal → LoadingModal 교체 적용, 소소한 스타일/간격 변경

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant OnboardingPage
    participant OnboardingStep
    participant OnboardingProfileClient
    participant UploadButton
    participant useProfileImageUpload

    User->>OnboardingPage: 온보딩 시작
    OnboardingPage->>OnboardingStep: 현재 단계 렌더링
    User->>OnboardingStep: 다음 클릭
    OnboardingPage->>OnboardingPage: 단계 증가 또는 완료 처리
    OnboardingPage->>OnboardingProfileClient: 프로필 단계 진입
    User->>OnboardingProfileClient: 이름 입력 및 업로드 모달 열기
    OnboardingProfileClient->>UploadButton: 업로드 시트 표시
    User->>UploadButton: 앨범 선택 또는 기본 이미지 선택
    UploadButton->>useProfileImageUpload: 파일 전달 또는 기본 적용 호출
    useProfileImageUpload-->>OnboardingProfileClient: 이미지 URL 및 로딩 상태 반환
    OnboardingProfileClient->>User: 미리보기 및 완료(로컬 저장 후 라우팅)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 깡충깡충 온보딩 길을 걷네
이름 한 글자, 사진 한 번 툭
점들이 모여 길이 되고
로더는 빙글 돌아 기다림을 달래네
당근으로 축하할게요 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 '온보딩 UI'는 변경사항의 핵심 내용인 온보딩 페이지 UI와 프로필 이미지 업로드 기능 추가를 명확하게 요약합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14e162c and 8a4aa56.

📒 Files selected for processing (1)
  • src/hooks/useProfileImageUpload.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/hooks/useProfileImageUpload.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@idid10 idid10 requested a review from lemoncurdyogurt January 4, 2026 15:16
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/styles/globals.css (1)

99-120: 중복된 유틸리티 블록을 제거해야 합니다.

99-120번 줄의 @layer utilities 블록이 50-97번 줄의 내용과 완전히 중복됩니다. .scrollbar-hide.bg-radial-yellowgreen-mintgreen 유틸리티가 두 번 정의되어 있습니다.

중복된 블록을 제거하고 새로운 유틸리티(.bg-dim-01, .backdrop-blur-7)만 122-130번 줄의 별도 블록에 유지하는 것이 좋습니다.

🔎 수정 제안
-@layer utilities {
-  .scrollbar-hide {
-    -ms-overflow-style: none; /* IE and Edge */
-    scrollbar-width: none; /* Firefox */
-  }
-  .scrollbar-hide::-webkit-scrollbar {
-    display: none; /* Chrome, Safari, Opera*/
-  }
-  .bg-radial-yellowgreen-mintgreen {
-    background:
-      radial-gradient(
-        39.1% 31.12% at 80.25% 2.51%,
-        rgba(246, 248, 173, 0.42) 57.26%,
-        rgba(255, 255, 255, 0.42) 100%
-      ),
-      radial-gradient(
-        39.99% 49.21% at 23.64% 2.72%,
-        rgba(66, 240, 158, 0.66) 21.15%,
-        rgba(255, 255, 255, 0.87) 93.53%
-      );
-  }
-}
-
 @layer utilities {
   .bg-dim-01 {
     background-color: var(--color-dim-01);
   }

   .backdrop-blur-7 {
     backdrop-filter: blur(7px);
   }
 }
🧹 Nitpick comments (11)
src/components/onboarding/ProgressDots.tsx (1)

10-23: className 중복 코드 리팩토링 고려

조건부 className에서 h-2.5 w-2.5 rounded-full이 양쪽 분기에 중복됩니다. 가독성과 유지보수성 향상을 위해 공통 클래스를 분리하는 것을 고려해보세요.

🔎 제안하는 리팩토링
         {Array.from({ length: total }).map((_, i) => {
           const isActive = i <= current;
 
           return (
             <span
               key={i}
-              className={
-                isActive
-                  ? "bg-mint-01 h-2.5 w-2.5 rounded-full"
-                  : "bg-neutral-08 h-2.5 w-2.5 rounded-full"
-              }
+              className={`h-2.5 w-2.5 rounded-full ${
+                isActive ? "bg-mint-01" : "bg-neutral-08"
+              }`}
             />
           );
         })}
src/components/common/NameInput.tsx (1)

17-22: 매직 넘버 문서화 권장

라인 21의 36은 입력 필드의 패딩값으로 추정되지만 명확하지 않습니다. 코드 가독성을 위해 상수로 추출하거나 주석을 추가하는 것을 고려해보세요.

🔎 제안하는 개선
+const PADDING_WIDTH = 36; // px-4 (16px * 2) + border/extra spacing
+
 const NameInput = ({ value, onChange }: NameInputProps) => {
   const spanRef = useRef<HTMLSpanElement>(null);
   const [inputWidth, setInputWidth] = useState<number>(INITIAL_WIDTH);
 
   useEffect(() => {
     if (!spanRef.current) return;
 
     const spanWidth = spanRef.current.offsetWidth;
-    setInputWidth(spanWidth + 36);
+    setInputWidth(spanWidth + PADDING_WIDTH);
   }, [value]);
src/components/onboarding/OnboardingStep.tsx (2)

18-18: 모바일 뷰포트 높이 처리 개선 고려

h-screen 대신 h-dvh 사용을 고려해보세요. 모바일 브라우저의 주소 표시줄로 인한 레이아웃 이슈를 방지할 수 있습니다. 상위 온보딩 페이지(src/app/onboarding/page.tsx)에서 이미 min-h-dvh를 사용하고 있어 일관성 측면에서도 유리합니다.

🔎 제안하는 수정
-    <main className="relative flex h-screen flex-col bg-white pt-[558px]">
+    <main className="relative flex h-dvh flex-col bg-white pt-[558px]">

19-33: 배경 오프셋 매직 넘버 상수화 고려

-translate-y-[110px]가 라인 25와 30에서 중복됩니다. 상수로 추출하거나 공통 클래스로 정의하면 유지보수가 용이해집니다.

🔎 제안하는 개선
 const OnboardingStep = ({ stepName, isLast, onNext }: Props) => {
   const { title, background } = onboardingContents[stepName];
+  const backgroundOffsetClass = "-translate-y-[110px]";
 
   return (
     <main className="relative flex h-screen flex-col bg-white pt-[558px]">
       {background.type === "image" && (
         <Image
           src={background.src}
           alt=""
           fill
           priority
-          className="pointer-events-none -translate-y-[110px] object-cover object-top"
+          className={`pointer-events-none ${backgroundOffsetClass} object-cover object-top`}
         />
       )}
       {background.type === "class" && (
         <div
-          className={`pointer-events-none absolute -translate-y-[110px] ${background.className}`}
+          className={`pointer-events-none absolute ${backgroundOffsetClass} ${background.className}`}
           aria-hidden
         />
       )}
src/config/onboardingSteps.ts (1)

1-6: 타입 정의가 잘 구성되어 있습니다.

as const 단언을 사용한 튜플 정의와 OnboardingStepName 타입 추출 패턴이 적절합니다. 다만, OnboardingBackgroundtype: "class" 변형은 현재 사용되지 않고 있습니다. 향후 확장을 위한 것이라면 괜찮지만, 사용 계획이 없다면 YAGNI 원칙에 따라 제거를 고려해볼 수 있습니다.

src/components/onboarding/UploadAlert.tsx (3)

28-34: onClose() 호출이 중복될 수 있습니다.

handleFileChange에서 onSelectAlbum(file) 호출 후 onClose()를 호출하고 있지만, OnboardingProfile.tsxonSelectAlbum 콜백에서도 이미 setIsUploadOpen(false)를 호출합니다. 중복 호출은 현재 문제가 되지 않지만, 한 곳에서만 닫기 로직을 관리하는 것이 좋습니다.

또한, 동일한 파일을 다시 선택할 경우 onChange 이벤트가 발생하지 않습니다. input의 value를 초기화해야 합니다.

🔎 수정 제안
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
-   if (!file) return;
+   if (!file) return;
+   
+   e.target.value = ""; // 동일 파일 재선택 허용

    onSelectAlbum(file);
-   onClose();
+   // onClose는 부모에서 처리
  };

56-62: 파일 크기 검증이 없습니다.

accept="image/*"로 이미지 파일만 허용하고 있지만, 파일 크기에 대한 제한이 없습니다. 대용량 이미지 업로드 시 성능 문제나 서버 오류가 발생할 수 있으므로, 클라이언트 측에서 파일 크기를 검증하는 것을 권장합니다.


36-43: 접근성 개선을 고려해보세요.

모달에 role="dialog", aria-modal="true", aria-labelledby 속성이 없습니다. 또한 모달이 열릴 때 포커스 트래핑이 구현되어 있지 않아 키보드 사용자의 접근성이 제한될 수 있습니다.

src/components/common/AlertModal.tsx (3)

38-43: 배경 클릭으로 모달이 닫히지 않습니다.

UploadAlert은 배경 클릭 시 onClose를 호출하지만, AlertModal은 배경 클릭 핸들러가 없습니다. 컴포넌트 간 일관성을 위해 배경 클릭 동작을 통일하거나, closeOnBackdrop prop을 추가하여 제어하는 것을 고려해보세요.


53-65: 로딩 상태와 메시지 렌더링 로직이 명확합니다.

isLoading && loadingImageSrc 조건으로 로딩 이미지를 표시하고, 그렇지 않으면 message를 표시하는 로직이 잘 구성되어 있습니다. 다만 isLoadingtrue이지만 loadingImageSrc가 없는 경우 아무것도 표시되지 않으므로, 이 케이스에 대한 폴백(예: 기본 스피너)을 고려해볼 수 있습니다.


54-58: <img> 태그 대신 Next.js의 Image 컴포넌트 사용을 고려해보세요.

Next.js 프로젝트에서는 이미지 최적화를 위해 next/imageImage 컴포넌트를 사용하는 것이 권장됩니다. 단, GIF 파일의 경우 unoptimized prop이 필요할 수 있습니다.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33f859c and 74cb410.

⛔ Files ignored due to path filters (6)
  • public/images/onboarding-step-1.png is excluded by !**/*.png
  • public/images/onboarding-step-2.png is excluded by !**/*.png
  • public/images/onboarding-step-3.png is excluded by !**/*.png
  • public/loading.gif is excluded by !**/*.gif
  • src/assets/onboarding_profile.svg is excluded by !**/*.svg
  • src/assets/photo.svg is excluded by !**/*.svg
📒 Files selected for processing (16)
  • src/app/login/page.tsx
  • src/app/onboarding/page.tsx
  • src/app/onboarding/profile/page.tsx
  • src/app/page.tsx
  • src/components/common/AlertModal.tsx
  • src/components/common/NameInput.tsx
  • src/components/onboarding/OnboardingProfile.tsx
  • src/components/onboarding/OnboardingStep.tsx
  • src/components/onboarding/ProfileImagePicker.tsx
  • src/components/onboarding/ProgressDots.tsx
  • src/components/onboarding/UploadAlert.tsx
  • src/components/password/PasswordForm.tsx
  • src/config/onboardingSteps.ts
  • src/hooks/useFunnel.tsx
  • src/hooks/useProfileImageUpload.ts
  • src/styles/globals.css
💤 Files with no reviewable changes (1)
  • src/app/login/page.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
src/components/onboarding/OnboardingProfile.tsx (1)
src/hooks/useProfileImageUpload.ts (1)
  • useProfileImageUpload (5-30)
src/components/onboarding/OnboardingStep.tsx (1)
src/config/onboardingSteps.ts (2)
  • OnboardingStepName (2-2)
  • onboardingContents (8-36)
src/app/onboarding/page.tsx (3)
src/hooks/useFunnel.tsx (1)
  • useFunnel (12-36)
src/config/onboardingSteps.ts (2)
  • ONBOARDING_STEPS (1-1)
  • OnboardingStepName (2-2)
src/components/onboarding/ProgressDots.tsx (1)
  • ProgressDots (6-27)
🔇 Additional comments (9)
src/hooks/useProfileImageUpload.ts (1)

1-1: 클라이언트 컴포넌트 지시어가 적절하게 사용되었습니다.

이 훅은 React 상태를 사용하므로 "use client" 지시어가 올바르게 적용되었습니다.

src/styles/globals.css (2)

46-47: 새로운 dim 색상 변수가 적절하게 추가되었습니다.

--color-dim-00--color-dim-01 변수는 온보딩 UI의 반투명 배경 스타일링에 사용될 것으로 보이며, 기존 색상 팔레트와 일관성을 유지하고 있습니다.


122-130: 새로운 유틸리티 클래스가 잘 추가되었습니다.

.bg-dim-01.backdrop-blur-7 유틸리티는 모달과 온보딩 UI의 배경 효과를 위해 적절하게 정의되었습니다. AlertModal의 backdrop 기능과 잘 연동될 것으로 보입니다.

src/components/password/PasswordForm.tsx (1)

225-225: AlertModal에 backdrop prop이 적절하게 추가되었습니다.

새로운 backdrop 기능을 지원하기 위해 backdrop="default" prop이 추가되었습니다. 이는 AlertModal 컴포넌트의 업데이트된 API와 일치하며, 기존 모달 동작에 영향을 주지 않습니다.

src/app/onboarding/profile/page.tsx (1)

1-6: 온보딩 프로필 페이지가 올바르게 구현되었습니다.

Next.js 페이지 라우팅 규칙에 맞게 간결하게 구현되었으며, OnboardingProfileClient 컴포넌트를 적절하게 래핑하고 있습니다.

src/app/onboarding/page.tsx (1)

24-33: localStorage 키는 일관되게 사용 중

라인 28에서 사용하는 "onboardingDone" 키는 코드베이스 전체에서 유일하게 한 번만 사용됩니다. 다른 변형 형태(예: "onboarding-done", "onboarding_done")는 발견되지 않았으므로 키 일관성 문제는 없습니다.

src/config/onboardingSteps.ts (1)

8-36: LGTM!

Record<OnboardingStepName, ...> 타입을 사용하여 모든 스텝에 대한 콘텐츠가 정의되어야 함을 컴파일 타임에 보장하고 있습니다. 이미지 경로가 하드코딩되어 있으므로 해당 이미지 파일들이 /public/images/ 디렉토리에 존재하는지 확인해주세요.

src/components/onboarding/OnboardingProfile.tsx (1)

49-52: 이미지 선택 후 즉시 모달을 닫는 것이 UX에 적합한지 확인이 필요합니다.

onSelectAlbum에서 uploadImage(file)를 호출한 직후 setIsUploadOpen(false)로 모달을 닫고 있습니다. uploadImage는 비동기 함수이므로 업로드가 완료되기 전에 모달이 닫히며, 이후 별도의 로딩 모달이 표시됩니다. 현재 흐름이 의도된 것인지 확인해주세요.

src/hooks/useFunnel.tsx (1)

3-6: StepPropsname 속성이 Step 컴포넌트에서 사용되지 않습니다.

StepProps 인터페이스에 name이 정의되어 있지만, Step 컴포넌트는 children만 사용합니다. 이는 의도된 설계로 보이며(Funnel에서 props.name으로 필터링), 타입 정의가 정확합니다.

@lemoncurdyogurt lemoncurdyogurt added the feat 기능 추가 label Jan 6, 2026
Copy link
Member

@lemoncurdyogurt lemoncurdyogurt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소한 디자인 오류라던가, 퍼널로직 수정에 대해서 리뷰적어두었습니다!
dim관련해서 이전에 말했던 것이라서 이번에는 수정 꼭해주세요...!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/styles/globals.css (1)

45-107: 중복된 @layer utilities 블록 제거 필요

45-84번 줄과 86-107번 줄에 동일한 유틸리티 클래스가 중복 정의되어 있습니다. 이는 유지보수성을 저해하고 예상치 못한 스타일 충돌을 야기할 수 있습니다.

🔎 중복 제거 제안
-@layer utilities {
-  .scrollbar-hide {
-    -ms-overflow-style: none; /* IE and Edge */
-    scrollbar-width: none; /* Firefox */
-  }
-  .scrollbar-hide::-webkit-scrollbar {
-    display: none; /* Chrome, Safari, Opera*/
-  }
-  .bg-radial-yellowgreen-mintgreen {
-    background:
-      radial-gradient(
-        39.1% 31.12% at 80.25% 2.51%,
-        rgba(246, 248, 173, 0.42) 57.26%,
-        rgba(255, 255, 255, 0.42) 100%
-      ),
-      radial-gradient(
-        39.99% 49.21% at 23.64% 2.72%,
-        rgba(66, 240, 158, 0.66) 21.15%,
-        rgba(255, 255, 255, 0.87) 93.53%
-      );
-  }
-}
🤖 Fix all issues with AI Agents
In @package.json:
- Line 20: The dependency "@use-funnel/next" is a pre-1.0 replacement for the
deprecated "@toss/use-funnel"; before merging, verify migration compatibility by
running full integration and E2E tests against the app, add a CI job that
exercises funnel-related flows, pin the dependency (remove caret) or add a
shrinkwrap/lockfile policy to avoid accidental minor bumps, and document the
change and monitoring plan (including rollback steps) in the PR/README to ensure
safe production rollout.

In @src/app/page.tsx:
- Around line 1-4: RootPage currently does an unconditional redirect to "/login"
which blocks authenticated users and can cause redirect loops; update the server
component RootPage to check authentication (e.g., call getSession() or your auth
check) and perform conditional redirects: if no session redirect("/login"), if
session but onboarding incomplete redirect("/onboarding"), otherwise
redirect("/home"); reference and replace the current redirect call in RootPage
and use getSession (or your session-fetching helper) to determine the correct
branch.

In @src/components/common/LoadingModal.tsx:
- Line 13: The LoadingModal component declares and destructures a
loadingImageSrc prop but never uses it; remove this unused prop by deleting
loadingImageSrc from the props interface (e.g., LoadingModalProps) and from the
component parameter destructuring in the LoadingModal function, and remove any
related default or import usage; alternatively, if you intend to support a
custom image, implement usage by rendering an <img> or background using
loadingImageSrc inside LoadingModal instead—choose one approach and ensure no
references to loadingImageSrc remain elsewhere.

In @src/components/onboarding/UploadButton.tsx:
- Around line 28-34: handleFileChange currently reads the selected file and
calls onSelectAlbum/onClose but doesn't reset the file input, so selecting the
same file again won't trigger onChange; after processing the file (after
onSelectAlbum and before/after onClose) clear the input's value — locate the
input element referenced by handleFileChange (e.g., the HTMLInputElement from
the event) and set its value = '' (or use a ref to the input and reset
ref.current.value = '') so the input is reinitialized for subsequent same-file
selections.

In @src/hooks/useOnboardingFunnel.ts:
- Line 3: The import of useFunnel from "@use-funnel/next" in
useOnboardingFunnel.ts is incompatible with React 19 required by Next.js 15;
either (A) switch the import to "@use-funnel/browser" in useOnboardingFunnel.ts
when using the App Router to avoid the Next-specific package, (B) upgrade or
replace the dependency with a React 19‑compatible release of @use-funnel (check
npm/changelog and update package.json and lockfile), or (C) add targeted
compatibility tests for useFunnel (invoke the useFunnel hook inside the
onboarding hook and run the app/router integration tests) to validate it works
under React 19—choose the appropriate option and update the import/reference to
useFunnel (or the dependency version) accordingly.

In @src/hooks/usePasswordValidation.ts:
- Around line 7-9: The isValidPassword function was reduced to only checking
length which removes required checks for letters, digits, and special
characters; restore the stronger validation used in the commented code by
updating isValidPassword to enforce min length 8 plus at least one
lowercase/uppercase letter, one digit and one special character (use the same
regex/logic from the commented block), ensure the signup flow still calls this
function, and if a weaker check is needed for tests only gate it behind an
explicit TEST env flag or test helper so production logic remains strict.

In @src/hooks/useProfileImageUpload.ts:
- Around line 22-30: Remove the commented-out uploadImage function block in
useProfileImageUpload (the commented async uploadImage / setIsLoading /
uploadImageFile / setProfileImage / finally lines) to clean up unused commented
code; if you want to preserve the snippet for future reference, rely on Git
history or move it to a docs/snippets file, but do not keep it commented in the
hook source.
- Line 5: Remove the unused commented import in useProfileImageUpload: delete
the commented line referencing uploadImageFile so the file no longer contains a
stale "//import { uploadImageFile } from "@/utils/upload";" comment; if/when the
uploadImageFile utility is needed later, re-add it as an active import.
- Line 42: The function useProfileImageUpload contains a duplicated, unreachable
return (the second "return { profileImage, isLoading, uploadImage, resetImage }
as const;" after the earlier return); remove the redundant trailing return so
only the intended single return in useProfileImageUpload remains, ensuring no
other logic depends on the removed line.

In @src/pages/_app.tsx:
- Line 2: The import name is misspelled: change the Next.js font import from
localfont to localFont in the import declaration (the symbol currently imported
as localfont in src/pages/_app.tsx) and update any usages of the variable
(references to localfont throughout the file, e.g., where the font is used or
its className is referenced) to use localFont so the correct Next.js API name is
used consistently.
- Around line 6-11: The pretendard font is being redefined here via the
localfont(...) call (const pretendard) even though the same font is already
exported from src/styles/font.ts; remove this duplicate localfont definition and
instead import the existing exported symbol (e.g., pretendard) from your styles
module and use that imported constant in the _app component so only the single
exported font definition from styles/font.ts is used across the app.

In @src/styles/font.ts:
- Line 1: The import identifier is misspelled as localfont; update the import to
use the correct camelCase name localFont (import localFont from
"next/font/local") and rename any usages of localfont in this file (e.g., calls
like localfont({...}) or references to the variable) to localFont so TypeScript
compiles without error.

In @src/styles/globals.css:
- Around line 109-127: The .loader rule uses hardcoded colors; replace the
literal hex values (border: 5px solid #ffffff and border-bottom-color: #42f09e)
with the project theme CSS variables defined under @theme (e.g., use
var(--<appropriate-theme-bg>) for the border and
var(--<appropriate-theme-accent>) for the highlight) and move the .loader and
@keyframes rotation definitions into the @layer utilities block to keep
consistency with other utility classes; ensure the variable names match the
existing @theme names and update any references accordingly.
🧹 Nitpick comments (8)
src/hooks/usePasswordValidation.ts (1)

2-6: 주석 처리된 코드를 제거하는 것을 고려하세요.

사용하지 않는 주석 처리된 코드는 코드베이스를 복잡하게 만들고 향후 혼란을 초래할 수 있습니다. 이전 로직이 필요한 경우 Git 히스토리에서 확인할 수 있습니다.

🔎 제안된 수정 사항
-  // const isValidPassword = (pwd: string) => {
-  //   const hasLetter = /[A-Za-z]/.test(pwd);
-  //   const hasNumber = /\d/.test(pwd);
-  //   const hasSpecial = /[^A-Za-z0-9]/.test(pwd);
-  //   return pwd.length >= 8 && hasLetter && hasNumber && hasSpecial;
   const isValidPassword = (pwd: string) => {
     return pwd.length >= 8;
   };
src/components/onboarding/UploadButton.tsx (1)

39-39: Tailwind 클래스 사용을 권장합니다.

인라인 스타일 대신 Tailwind 클래스를 사용하여 일관성을 유지하는 것을 권장합니다.

🔎 수정 제안
     <div
       className="fixed inset-0 z-50 flex items-end justify-center"
-      style={{ backgroundColor: "rgba(36, 38, 40, 0.3)" }}
+      className="fixed inset-0 z-50 flex items-end justify-center bg-black/30"
       onClick={onClose}
src/hooks/useOnboardingFunnel.ts (1)

7-7: 타입 안전성 개선을 고려하세요.

OnboardingFunnelContextRecord<OnboardingStepName, object>로 정의되어 각 스텝의 컨텍스트가 제네릭한 object 타입입니다. 현재는 빈 객체 {}만 사용하므로 문제없지만, 향후 각 스텝별로 특정 데이터를 저장해야 할 경우 타입 안전성이 보장되지 않습니다.

필요 시 각 스텝별 구체적인 타입을 정의하는 것을 고려하세요.

src/pages/onboarding/index.tsx (1)

28-28: localStorage 키를 상수로 추출하는 것을 고려하세요.

"onboardingDone" 문자열이 하드코딩되어 있습니다. 다른 곳에서도 이 키를 사용할 가능성이 있다면 상수로 추출하여 재사용성과 유지보수성을 높이는 것이 좋습니다.

🔎 제안하는 리팩토링

상수 파일 또는 해당 파일 상단에 추가:

const ONBOARDING_DONE_KEY = "onboardingDone";

사용:

-        localStorage.setItem("onboardingDone", "true");
+        localStorage.setItem(ONBOARDING_DONE_KEY, "true");
src/components/onboarding/OnboardingStep.tsx (2)

21-21: 매직 넘버를 상수로 추출하는 것을 고려하세요.

w-[440px], pt-[558px], mt-[13px] 등 여러 하드코딩된 수치가 있습니다. 이들을 상수로 정의하면 유지보수성과 가독성이 향상됩니다. 또한 디자인 시스템 토큰으로 관리할 수 있다면 더욱 좋습니다.

🔎 제안하는 리팩토링
const ONBOARDING_CONTAINER_WIDTH = 440;
const ONBOARDING_CONTENT_PADDING_TOP = 558;
const ONBOARDING_MARGIN_TOP = 13;

또는 Tailwind config에 커스텀 스페이싱으로 추가하여 재사용 가능하게 만들 수 있습니다.


34-34: className 조합 시 유틸리티 함수 사용을 고려하세요.

템플릿 리터럴로 className을 직접 연결하는 것보다 clsx 또는 프로젝트에서 사용 중인 cn 유틸리티를 활용하면 조건부 클래스 처리와 가독성이 개선됩니다.

🔎 제안하는 리팩토링
-            className={`pointer-events-none absolute -translate-y-[110px] ${background.className}`}
+            className={cn("pointer-events-none absolute -translate-y-[110px]", background.className)}
src/constants/onboardingSteps.ts (2)

5-9: 타입 중복을 확인하세요.

OnboardingFunnelSteps 타입이 src/hooks/useOnboardingFunnel.tsOnboardingFunnelContext와 유사한 구조를 가지고 있습니다. 두 타입이 동일한 목적을 가진다면 하나로 통합하여 중복을 제거하는 것을 고려하세요.


11-13: OnboardingBackground의 "class" 타입이 현재 사용되지 않습니다.

OnboardingBackground에서 type: "class" 옵션을 정의했지만, 현재 모든 스텝이 type: "image"만 사용하고 있습니다. 향후 확장을 위한 것이라면 문제없지만, 사용하지 않는다면 제거를 고려하세요.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74cb410 and a4d14e0.

⛔ Files ignored due to path filters (4)
  • public/images/onboarding-step-1.svg is excluded by !**/*.svg
  • public/images/onboarding-step-2.svg is excluded by !**/*.svg
  • public/images/onboarding-step-3.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (25)
  • package.json
  • src/app/page.tsx
  • src/app/signup/agree/page.tsx
  • src/app/signup/password/page.tsx
  • src/components/common/LoadingModal.tsx
  • src/components/onboarding/OnboardingProfile.tsx
  • src/components/onboarding/OnboardingStep.tsx
  • src/components/onboarding/ProfileImagePicker.tsx
  • src/components/onboarding/ProgressDots.tsx
  • src/components/onboarding/UploadButton.tsx
  • src/components/password/PasswordForm.tsx
  • src/components/signup/ProfileForm.tsx
  • src/constants/agreement.ts
  • src/constants/alert.ts
  • src/constants/onboardingSteps.ts
  • src/hooks/useOnboardingFunnel.ts
  • src/hooks/usePasswordValidation.ts
  • src/hooks/useProfileImageUpload.ts
  • src/pages/_app.tsx
  • src/pages/onboarding/index.tsx
  • src/pages/onboarding/profile.tsx
  • src/styles/font.ts
  • src/styles/globals.css
  • src/types/agreement.type.ts
  • src/utils/upload.ts
✅ Files skipped from review due to trivial changes (1)
  • src/utils/upload.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/components/onboarding/ProfileImagePicker.tsx
  • src/components/onboarding/ProgressDots.tsx
  • src/components/onboarding/OnboardingProfile.tsx
  • src/components/password/PasswordForm.tsx
🧰 Additional context used
🧬 Code graph analysis (5)
src/constants/agreement.ts (1)
src/types/agreement.type.ts (1)
  • AgreementKey (1-7)
src/pages/_app.tsx (1)
src/styles/font.ts (1)
  • pretendard (3-8)
src/hooks/useOnboardingFunnel.ts (1)
src/constants/onboardingSteps.ts (1)
  • OnboardingStepName (3-3)
src/app/signup/agree/page.tsx (2)
src/types/agreement.type.ts (1)
  • AgreementKey (1-7)
src/constants/agreement.ts (1)
  • INITIAL_AGREEMENTS (12-19)
src/components/onboarding/OnboardingStep.tsx (1)
src/constants/onboardingSteps.ts (2)
  • OnboardingStepName (3-3)
  • ONBOARDING_CONTENTS (15-43)
🪛 Biome (2.1.2)
src/hooks/useProfileImageUpload.ts

[error] 42-42: This code will never be reached ...

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

🔇 Additional comments (12)
src/constants/agreement.ts (1)

1-19: LGTM! 타입 안전한 agreement 상수 정의

타입 정의와 상수가 잘 분리되어 있고, 타입 안전성이 보장됩니다.

src/components/signup/ProfileForm.tsx (1)

12-12: LoadingModal 컴포넌트 인터페이스 확인 완료

LoadingModal 컴포넌트는 isOpen, title, message, onClose props를 모두 지원합니다. ProfileForm.tsx에서도 이 모든 props를 올바르게 전달하고 있으므로 호환성 문제가 없습니다.

src/types/agreement.type.ts (1)

1-7: LGTM!

타입 정의가 깔끔하고 각 약관의 필수/선택 여부가 주석으로 잘 문서화되어 있습니다.

src/constants/alert.ts (1)

1-7: LGTM!

백드롭 타입과 클래스 매핑이 잘 구조화되어 있습니다. Record 타입을 사용하여 타입 안전성을 보장하고 있습니다.

src/app/signup/agree/page.tsx (1)

12-19: LGTM!

상수와 타입을 중앙 집중화하여 코드 중복을 제거한 좋은 리팩토링입니다. INITIAL_AGREEMENTS를 초기 상태로 사용하는 것은 useState가 내부적으로 값을 복사하므로 안전합니다.

src/components/common/LoadingModal.tsx (1)

44-52: LGTM!

로딩 상태와 메시지 렌더링 조건 분기가 적절합니다. loader CSS 클래스가 globals.css(109-114행)에 정의되어 있습니다.

src/app/signup/password/page.tsx (1)

137-137: 변경사항이 적절합니다.

회원가입 완료 후 온보딩 페이지로 이동하도록 내비게이션 경로가 올바르게 업데이트되었습니다.

src/pages/onboarding/profile.tsx (1)

1-6: 구현이 적절합니다.

Next.js 페이지 패턴을 올바르게 따르고 있으며, 온보딩 프로필 컴포넌트를 깔끔하게 래핑하고 있습니다.

src/hooks/useProfileImageUpload.ts (1)

11-20: 이전 리뷰에서 지적된 여러 문제를 해결해주세요.

uploadImage 함수에는 다음과 같은 문제가 있습니다:

  1. 메모리 누수: URL.createObjectURL()로 생성된 Object URL이 해제되지 않습니다
  2. 파일 검증 부재: 파일 타입과 크기 제한이 없습니다
  3. 에러 처리 누락: 업로드 실패 시 처리가 없습니다
  4. 시뮬레이션 딜레이: 2초 지연은 실제 API 연동 전 임시 코드로 보입니다

이전 리뷰 코멘트에서 제공된 개선 방안을 참고하여 수정해주세요.

Likely an incorrect or invalid review comment.

src/pages/onboarding/index.tsx (1)

37-67: 구현이 잘 되어 있습니다.

페이지 구조와 퍼널 패턴 사용이 적절하며, 각 스텝 컴포넌트에 필요한 props를 올바르게 전달하고 있습니다.

src/components/onboarding/OnboardingStep.tsx (1)

39-47: 콘텐츠 레이아웃과 버튼 로직이 잘 구현되어 있습니다.

z-10을 사용하여 배경 위에 콘텐츠를 올바르게 배치했고, whitespace-pre-line으로 타이틀의 줄바꿈을 지원하며, isLastStep에 따라 버튼 텍스트를 적절하게 변경하고 있습니다.

src/constants/onboardingSteps.ts (1)

24-40: 모든 온보딩 이미지 파일이 정상적으로 존재합니다.

/images/onboarding-step-{1,2,3}.svg 경로는 모두 public/images/ 디렉토리에 존재하며, Next.js의 정적 자산 서빙 방식에 맞게 올바르게 구성되어 있습니다. 런타임에 404 오류가 발생하지 않습니다.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI Agents
In @src/app/page.tsx:
- Around line 1-2: HomePage is unprotected: add an authentication guard that
ties into the existing onboarding flow under src/pages/onboarding and
src/components/onboarding by checking the user's auth/onboarding state and
redirecting to the onboarding route or /login if needed; implement this either
in a middleware (middleware.ts) that protects the root route, in the root/layout
(src/app/layout.tsx) by awaiting/validating session and redirecting, or directly
inside the HomePage component (export default function HomePage) by calling the
onboarding/auth hook and issuing a redirect when unauthenticated or
onboarding-incomplete; ensure the redirect target matches the onboarding flow
and reuse the existing onboarding hooks/components for state checks so the home
page is only accessible after successful auth/onboarding.

In @src/components/onboarding/OnboardingProfile.tsx:
- Around line 70-77: The LoadingModal usage currently passes a no-op onClose={()
=> {}} causing the "취소하기" button to do nothing; either remove the confirmLabel
prop or hide/disable the cancel button when cancellation is not allowed, or
implement actual cancel/upload-abort logic and wire it into the onClose handler
(e.g., call your upload abort function or setLoading(false)); update the
LoadingModal invocation (symbols: LoadingModal, props confirmLabel and onClose)
accordingly and consider removing confirmLabel when showing an uninterruptible
loading state for better UX.
- Around line 26-29: handleCreateProfile currently navigates away without
persisting the entered profile, so capture and save the data before routing: in
handleCreateProfile (and where isNameValid is checked) gather the name and image
values and call the profile persistence method (e.g., an API function like
saveProfile or a Redux/Context action such as dispatch(setProfile(...))) and
await its result; only call router.replace("/login") after a successful save,
show/handle errors on failure, and disable the submit action while the save is
in progress to avoid double submissions.
- Around line 57-68: The onSelectAlbum handler calls uploadImage without
handling errors so the modal closes even on failure; update the onSelectAlbum to
await uploadImage (or handle its Promise), catch any upload errors, keep the
modal open on failure (do not call setIsUploadOpen(false) on error), and surface
the error to the user (e.g., show a toast or set an error state) so failures are
visible; also ensure onSelectDefault still closes via resetImage and
setIsUploadOpen(false).
- Around line 23-24: Remove the unreachable duplicate return in the
useProfileImageUpload hook: inside src/hooks/useProfileImageUpload.ts delete the
second return that repeats the object (the one returning "return { profileImage,
isLoading, uploadImage, resetImage } as const;") so only the first valid "return
{ profileImage, isLoading, uploadImage, resetImage };" remains; verify the hook
still exports the same symbols (profileImage, isLoading, uploadImage,
resetImage).
🧹 Nitpick comments (1)
src/components/onboarding/OnboardingProfile.tsx (1)

23-24: 메모리 누수 방지를 위해 Object URL 정리를 고려하세요.

useProfileImageUpload 훅이 URL.createObjectURL을 사용하여 이미지 URL을 생성하는데, 이러한 Object URL은 사용 후 URL.revokeObjectURL로 정리해야 메모리 누수를 방지할 수 있습니다. 컴포넌트가 언마운트되거나 새 이미지가 업로드될 때 이전 Object URL을 정리하는 것을 권장합니다.

useProfileImageUpload 훅에 cleanup 로직을 추가하는 것을 고려해보세요:

useEffect(() => {
  return () => {
    if (profileImage && profileImage.startsWith('blob:')) {
      URL.revokeObjectURL(profileImage);
    }
  };
}, [profileImage]);
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4d14e0 and 1483d0c.

📒 Files selected for processing (2)
  • src/app/page.tsx
  • src/components/onboarding/OnboardingProfile.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/onboarding/OnboardingProfile.tsx (1)
src/hooks/useProfileImageUpload.ts (1)
  • useProfileImageUpload (7-43)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/components/common/LoadingModal.tsx (1)

13-13: 주석 처리된 코드를 제거하거나 TODO로 명시하세요.

loadingImageSrc prop이 주석 처리되어 있습니다. 향후 사용 계획이 있다면 TODO 주석으로 명시하고, 그렇지 않다면 완전히 제거하는 것이 코드 가독성과 유지보수성에 더 좋습니다.

♻️ 제안: 주석 처리된 코드 제거
 interface LoadingModalProps {
   isOpen: boolean;
   title: string;
   message?: string;
   confirmLabel?: string;
   onClose: () => void;
   backdrop?: AlertBackdrop;
   isLoading?: boolean;
-  //loadingImageSrc?: string;
 }

 const LoadingModal = ({
   isOpen,
   title,
   message,
   confirmLabel = "확인",
   onClose,
   backdrop = "default",
   isLoading = false,
-  //loadingImageSrc,
 }: LoadingModalProps) => {

Also applies to: 24-24

src/app/onboarding/page.tsx (1)

19-21: 불필요한 타입 단언을 제거하세요.

ONBOARDING_STEPSas const로 선언되어 있고 currentStepIndex가 배열 범위 내에서 관리되므로, 타입 단언 없이도 TypeScript가 올바른 타입을 추론할 수 있습니다.

♻️ 제안: 타입 단언 제거
-  const currentStepName = ONBOARDING_STEPS[
-    currentStepIndex
-  ] as OnboardingStepName;
+  const currentStepName = ONBOARDING_STEPS[currentStepIndex];
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1483d0c and 6317409.

📒 Files selected for processing (10)
  • src/app/onboarding/page.tsx
  • src/app/onboarding/profile/page.tsx
  • src/app/page.tsx
  • src/components/common/LoadingModal.tsx
  • src/components/onboarding/OnboardingProfile.tsx
  • src/components/onboarding/OnboardingStep.tsx
  • src/components/onboarding/ProgressDots.tsx
  • src/components/onboarding/UploadButton.tsx
  • src/hooks/useProfileImageUpload.ts
  • src/styles/globals.css
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/app/page.tsx
  • src/styles/globals.css
  • src/components/onboarding/ProgressDots.tsx
  • src/components/onboarding/OnboardingStep.tsx
  • src/hooks/useProfileImageUpload.ts
  • src/components/onboarding/UploadButton.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
src/app/onboarding/page.tsx (2)
src/constants/onboardingSteps.ts (2)
  • ONBOARDING_STEPS (1-1)
  • OnboardingStepName (3-3)
src/components/onboarding/ProgressDots.tsx (1)
  • ProgressDots (6-23)
src/components/onboarding/OnboardingProfile.tsx (1)
src/hooks/useProfileImageUpload.ts (1)
  • useProfileImageUpload (7-53)
src/components/common/LoadingModal.tsx (1)
src/constants/alert.ts (2)
  • AlertBackdrop (1-1)
  • ALERT_BACKDROP_CLASS (3-7)
🔇 Additional comments (5)
src/app/onboarding/profile/page.tsx (1)

1-6: LGTM!

Next.js App Router 패턴을 올바르게 따르는 깔끔한 페이지 래퍼입니다.

src/app/onboarding/page.tsx (1)

24-36: LGTM!

typeof window 체크를 통한 SSR 안전 처리와 온보딩 완료 후 프로필 페이지로의 네비게이션 로직이 올바르게 구현되어 있습니다.

src/components/onboarding/OnboardingProfile.tsx (3)

26-45: 프로필 저장 로직이 올바르게 구현되었습니다.

이전 리뷰에서 지적된 데이터 손실 문제가 해결되었습니다. 저장 성공 후에만 페이지 이동이 발생하며, 에러 처리도 포함되어 있습니다. TODO 주석에 명시된 대로 실제 API 연동 시 이 부분을 교체하시면 됩니다.


76-83: 이미지 업로드 에러 처리가 올바르게 추가되었습니다.

이전 리뷰에서 지적된 에러 처리 누락 문제가 해결되었습니다. 업로드 실패 시 모달이 열린 상태로 유지되고 에러가 로깅됩니다.


90-97: 로딩 모달의 취소 기능이 올바르게 구현되었습니다.

이전 리뷰에서 지적된 빈 onClose 핸들러 문제가 해결되었습니다. cancelUpload 함수를 통해 사용자가 업로드를 취소할 수 있습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/components/common/LoadingModal.tsx (1)

42-44: 접근성 개선: 로더에 레이블을 추가하세요.

로딩 상태를 나타내는 span 요소에 스크린 리더를 위한 접근 가능한 레이블이 없습니다.

♻️ 제안하는 접근성 개선
-            <span className="loader" />
+            <span className="loader" role="status" aria-label="로딩 중" />
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6317409 and 14cfb75.

📒 Files selected for processing (5)
  • src/app/signup/agree/page.tsx
  • src/app/signup/password/page.tsx
  • src/components/common/LoadingModal.tsx
  • src/components/password/PasswordForm.tsx
  • src/components/signup/ProfileForm.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/password/PasswordForm.tsx
  • src/app/signup/password/page.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/common/LoadingModal.tsx (1)
src/constants/alert.ts (2)
  • AlertBackdrop (1-1)
  • ALERT_BACKDROP_CLASS (3-7)
🔇 Additional comments (9)
src/app/signup/agree/page.tsx (1)

12-19: 상수 추출 리팩토링이 올바르게 구현됨

임포트된 AGREEMENTS, INITIAL_AGREEMENTS, AgreementKey의 구조를 확인한 결과, 모두 예상되는 형태와 일치합니다:

  • INITIAL_AGREEMENTSRecord<AgreementKey, boolean> 타입으로 모든 값이 false로 초기화됨
  • AGREEMENTS{ key: AgreementKey, label: string }[] 구조로 6개의 약관을 정의
  • AgreementKey는 "service", "finance", "personalReq", "personalOpt1", "personalOpt2", "marketing"을 포함

상수 추출을 통한 DRY 원칙 적용과 타입 안정성이 잘 유지되었습니다.

src/components/signup/ProfileForm.tsx (2)

12-12: LGTM! 컴포넌트 임포트 변경이 올바릅니다.

AlertModal에서 LoadingModal로의 임포트 변경이 적절합니다.


248-257: LGTM! 모달 컴포넌트 교체가 적절합니다.

LoadingModal 사용이 올바르게 적용되었습니다. 명시적으로 설정하지 않은 backdropisLoading props는 기본값을 사용하며, 이는 인증 결과 표시 용도로 적절합니다.

src/components/common/LoadingModal.tsx (6)

3-4: LGTM! 백드롭 상수 임포트가 적절합니다.

ALERT_BACKDROP_CLASSAlertBackdrop 타입 임포트가 올바르게 추가되었습니다.


5-13: LGTM! 인터페이스 확장이 적절합니다.

LoadingModalProps 인터페이스가 올바르게 정의되었습니다. message를 선택적으로 만들고 backdropisLoading props를 추가한 것이 적절합니다.


15-23: LGTM! 컴포넌트 시그니처가 올바릅니다.

함수 시그니처와 기본값 설정(backdrop = "default", isLoading = false)이 적절합니다.


28-30: LGTM! 백드롭 클래스 적용이 올바릅니다.

ALERT_BACKDROP_CLASS[backdrop]를 사용한 동적 클래스 적용이 타입 안전하게 구현되었습니다.


67-67: LGTM! Export 문이 올바릅니다.

컴포넌트 이름 변경에 맞춰 export가 올바르게 업데이트되었습니다.


42-50: loader CSS 클래스는 src/styles/globals.css의 110-120번 줄에서 정의되어 있으며, 회전 애니메이션과 함께 올바르게 구현되어 있습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/components/onboarding/OnboardingProfile.tsx:
- Around line 23-24: The useProfileImageUpload hook leaks blob URLs; ensure any
existing object URL stored in profileImage (or equivalent state) is revoked with
URL.revokeObjectURL before replacing it in uploadImage and before clearing it in
resetImage, and add a useEffect cleanup in useProfileImageUpload that revokes
the current blob URL on unmount; update uploadImage to revoke the previous URL
before setting a new one, update resetImage to revoke then clear the state, and
add a cleanup callback that revokes profileImage when the hook/component
unmounts.
🧹 Nitpick comments (3)
src/components/onboarding/OnboardingProfile.tsx (1)

76-83: 중복된 상태 업데이트.

UploadButton 컴포넌트의 handleFileChange(line 53)에서 파일 선택 직후 onClose()가 이미 호출됩니다. 따라서 line 79의 setIsUploadOpen(false)는 중복 호출입니다.

♻️ 중복 제거 제안
 onSelectAlbum={async file => {
   try {
     await uploadImage(file);
-    setIsUploadOpen(false);
   } catch (error) {
     console.error("이미지 업로드 실패:", error);
   }
 }}
src/components/onboarding/UploadButton.tsx (2)

23-36: 포커스 관리 개선 권장.

모달이 열릴 때 컨테이너 div에 포커스를 주고 있지만, 첫 번째 인터랙티브 요소(버튼)에 포커스를 주는 것이 접근성 측면에서 더 좋습니다.

♻️ 첫 번째 버튼에 포커스 주기
+const firstButtonRef = useRef<HTMLButtonElement | null>(null);

 useEffect(() => {
   if (!isOpen) return;

   const handleEscape = (e: KeyboardEvent) => {
     if (e.key === "Escape") onClose();
   };

   document.addEventListener("keydown", handleEscape);
-  modalRef.current?.focus();
+  firstButtonRef.current?.focus();

   return () => {
     document.removeEventListener("keydown", handleEscape);
   };
 }, [isOpen, onClose]);

버튼에 ref 추가:

 <button
   type="button"
+  ref={firstButtonRef}
   className="bg-neutral-11 text-button-sb text-mint-01 ..."
   onClick={handleAlbumClick}
 >

56-64: 인라인 스타일을 Tailwind로 대체 고려.

backgroundColor가 인라인 스타일로 지정되어 있습니다. 프로젝트 일관성을 위해 Tailwind의 bg-black/30 또는 커스텀 색상 클래스 사용을 고려해 보세요.

♻️ Tailwind 클래스 사용
 <div
   className="fixed inset-0 z-50 flex items-end justify-center bg-black/30"
-  style={{ backgroundColor: "rgba(36, 38, 40, 0.3)" }}
   onClick={onClose}
   ...
 >

정확한 색상이 필요하다면 tailwind.config에 커스텀 색상을 추가하세요.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14cfb75 and f1559ac.

📒 Files selected for processing (3)
  • src/components/onboarding/OnboardingProfile.tsx
  • src/components/onboarding/OnboardingStep.tsx
  • src/components/onboarding/UploadButton.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/onboarding/OnboardingStep.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/onboarding/OnboardingProfile.tsx (2)
src/hooks/useProfileImageUpload.ts (1)
  • useProfileImageUpload (7-53)
src/components/common/FullButton.tsx (1)
  • FullButton (10-35)
src/components/onboarding/UploadButton.tsx (1)
src/components/common/FullButton.tsx (1)
  • FullButton (10-35)
🔇 Additional comments (6)
src/components/onboarding/OnboardingProfile.tsx (3)

1-14: LGTM!

임포트 구조와 클라이언트 컴포넌트 선언이 적절합니다.


43-44: 프로필 생성 후 라우팅 경로 확인 필요.

온보딩 프로필 생성 후 /login으로 이동하는 것이 의도된 흐름인지 확인이 필요합니다. 일반적으로 온보딩 완료 후에는 홈 페이지나 다음 단계로 이동하는 것이 자연스럽습니다.


90-97: LGTM!

LoadingModal 구성이 적절합니다. 다만 cancelUpload가 현재는 isLoading 상태만 변경하므로, 실제 API 연동 시 AbortController 등을 활용한 요청 취소 로직이 필요합니다.

src/components/onboarding/UploadButton.tsx (3)

1-12: LGTM!

클라이언트 컴포넌트 선언과 Props 인터페이스 정의가 명확합니다.


40-54: LGTM!

파일 입력 초기화 로직이 적절합니다. fileInputRef.current.value = ""를 통해 동일한 파일을 다시 선택할 수 있게 처리한 점이 좋습니다.


71-111: LGTM!

모달 내부 구조가 깔끔하고 FullButton 컴포넌트를 적절히 활용하고 있습니다. ARIA 속성들이 잘 설정되어 있어 접근성이 양호합니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/common/FullButton.tsx (1)

16-32: h-14.5는 유효하지 않은 Tailwind 클래스입니다. 일관되게 수정이 필요합니다

h-14.5는 Tailwind v4 기본 스케일에 없으며 설정 파일에도 정의되지 않았습니다. 따라서 버튼 요소에는 높이가 적용되지 않으며, 실제 높이는 내부 div의 h-[58px]에만 의존합니다.

버튼 요소의 높이를 명확히 보장하려면 다음 중 하나를 선택하세요:

  • 버튼과 내부 div 모두에 h-[58px]를 사용 (권장)
  • 또는 globals.css의 @theme 블록에 --height-14-5: 3.625rem; 같은 커스텀 값 정의
🧹 Nitpick comments (1)
src/components/common/FullButton.tsx (1)

24-33: 불필요한 래퍼 + text-button-sb 중복 적용이라 정리 권장

버튼 자체가 이미 items-center justify-center인데 내부에 높이 고정 래퍼(div)를 한 번 더 두고, text-button-sb도 stateClass/자식 span에 중복이라 유지보수성이 떨어집니다. (한 군데로 모으는 편이 안전)

제안 diff
-  const baseClass =
-    "flex h-14.5 w-full cursor-pointer items-center justify-center rounded-2xl px-3.5";
-  const activeClass = "bg-mint-01 text-white text-button-sb";
+  const baseClass =
+    "flex h-[58px] w-full cursor-pointer items-center justify-center rounded-2xl px-3.5";
+  const activeClass = "bg-mint-01 text-white";
   const inactiveClass =
-    "border border-neutral-08 bg-white text-neutral-06 text-button-sb";
+    "border border-neutral-08 bg-white text-neutral-06";
@@
     <button
       className={`${baseClass} ${stateClass} ${className}`}
       disabled={!isActive}
       {...props}
     >
-      <div className="flex h-[58px] items-center justify-center">
-        <span className="text-button-sb">{children}</span>
-      </div>
+      <span className="text-button-sb">{children}</span>
     </button>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1559ac and 142e82d.

📒 Files selected for processing (1)
  • src/components/common/FullButton.tsx

Copy link
Member

@lemoncurdyogurt lemoncurdyogurt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저번에 코드리뷰했던 부분 많이 수정해주셔서 감사합니다(_ _) 꾸벅
오늘 말씀드렸던 것처럼 개발자모드로 반응형 화면 고려해서 컴포넌트 너비라던가 높이 관련 요소들 재수정해주시면 감사하겠습니다아..!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

browser라서 일단 funnel/next 코드 지워주세요오...!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내가 지움Vv

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/components/onboarding/UploadButton.tsx (1)

47-54: onSelectAlbum 호출 후 onClose 중복 호출 가능성

onSelectAlbum이 비동기 함수로 전달될 경우, onClose()가 즉시 호출되어 모달이 먼저 닫힙니다. 현재 부모 컴포넌트(OnboardingProfile)에서도 setIsUploadOpen(false)를 호출하고 있어 중복 호출이 발생합니다.

모달 닫힘 시점을 부모가 제어하도록 onClose() 호출을 제거하는 것이 더 명확합니다.

♻️ 제안된 수정
 const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
   const file = e.target.files?.[0];
   if (!file) return;

   onSelectAlbum(file);
   e.target.value = "";
-  onClose();
 };
src/components/onboarding/OnboardingProfile.tsx (3)

34-42: 에러 발생 시 사용자 피드백이 없습니다.

현재 에러 발생 시 console.error만 호출하고 사용자에게 아무런 피드백을 주지 않습니다. API로 교체 시 사용자에게 실패 메시지를 표시하는 것이 좋습니다.


62-64: 반응형 레이아웃 개선 필요

px-[118px]는 고정된 값으로 작은 화면에서 레이아웃이 깨질 수 있습니다. 비율 기반 패딩이나 max-width를 사용하는 것이 더 안정적입니다.

♻️ 제안된 수정
-            <div className="w-full px-[118px]">
+            <div className="mx-auto w-full max-w-[200px]">
               <NameInput value={name} onChange={setName} />
             </div>

80-87: setIsUploadOpen(false) 중복 호출

UploadButton 내부에서 onSelectAlbum 호출 후 onClose()를 이미 실행하므로, Line 83의 setIsUploadOpen(false)는 중복됩니다. UploadButton에서 onClose() 호출을 제거하면 이 코드가 의도대로 동작합니다.

src/app/signup/agree/page.tsx (1)

12-23: INITIAL_AGREEMENTS 초기화 및 필수 동의 키 관리 개선

타입 안전성은 이미 AgreementKey 명시 지정으로 보장되고 있으나, 다음 두 가지 개선이 권장됩니다:

  • useState(INITIAL_AGREEMENTS): 상수 객체를 직접 공유하지 말고 useState<Record<AgreementKey, boolean>>(() => ({ ...INITIAL_AGREEMENTS }))로 초기화하여 방어적으로 처리
  • isConfirmActive 하드코딩 제거: 필수 동의 키 리스트(service, finance, personalRequired)를 REQUIRED_AGREEMENT_KEYS 같은 상수로 별도 관리하고, 동적으로 검증하면 필수 조건 변경 시 drift 위험 제거
최소 개선 방안 (diff)
   const [agreements, setAgreements] =
-    useState<Record<AgreementKey, boolean>>(INITIAL_AGREEMENTS);
+    useState<Record<AgreementKey, boolean>>(() => ({ ...INITIAL_AGREEMENTS }));

그리고 src/constants/agreement.ts에 추가:

+ export const REQUIRED_AGREEMENT_KEYS: AgreementKey[] = ['service', 'finance', 'personalRequired'];

page.tsx에서:

- const isConfirmActive =
-   agreements.service && agreements.finance && agreements.personalRequired;
+ const isConfirmActive = REQUIRED_AGREEMENT_KEYS.every(key => agreements[key]);
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58563bc and 14e162c.

📒 Files selected for processing (15)
  • src/app/login/page.tsx
  • src/app/login/phone/page.tsx
  • src/app/onboarding/page.tsx
  • src/app/password/find/page.tsx
  • src/app/password/find/result/page.tsx
  • src/app/signup/agree/page.tsx
  • src/components/common/NameInput.tsx
  • src/components/onboarding/OnboardingProfile.tsx
  • src/components/onboarding/OnboardingStep.tsx
  • src/components/onboarding/ProfileImagePicker.tsx
  • src/components/onboarding/ProgressDots.tsx
  • src/components/onboarding/UploadButton.tsx
  • src/constants/agreement.ts
  • src/constants/alert.ts
  • src/types/agreement.type.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/constants/agreement.ts
  • src/app/onboarding/page.tsx
  • src/components/onboarding/OnboardingStep.tsx
  • src/components/onboarding/ProgressDots.tsx
  • src/constants/alert.ts
  • src/components/onboarding/ProfileImagePicker.tsx
  • src/types/agreement.type.ts
  • src/components/common/NameInput.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
src/app/signup/agree/page.tsx (2)
src/types/agreement.type.ts (1)
  • AgreementKey (1-7)
src/constants/agreement.ts (1)
  • INITIAL_AGREEMENTS (12-19)
src/components/onboarding/OnboardingProfile.tsx (3)
src/hooks/useProfileImageUpload.ts (1)
  • useProfileImageUpload (7-53)
src/components/common/NameInput.tsx (1)
  • NameInput (13-40)
src/components/common/FullButton.tsx (1)
  • FullButton (10-35)
src/components/onboarding/UploadButton.tsx (1)
src/components/common/FullButton.tsx (1)
  • FullButton (10-35)
🔇 Additional comments (10)
src/app/password/find/page.tsx (1)

7-7: LGTM! 일관된 레이아웃 개선입니다.

main 컨테이너에 w-full 추가는 반응형 디자인 일관성을 위한 적절한 변경입니다. PR 전반의 레이아웃 리팩토링과 일관성이 있습니다.

src/app/login/page.tsx (1)

13-13: LGTM! 일관된 레이아웃 패턴 적용입니다.

인증 페이지 전반에 걸쳐 동일한 w-full 패턴을 적용하여 일관성 있는 레이아웃을 구현하고 있습니다.

src/app/login/phone/page.tsx (1)

30-35: 배경 및 레이아웃 리팩토링이 잘 적용되었습니다.

배경 스타일을 outer container로 이동하고 inner wrapper를 full-width로 변경한 것은 반응형 디자인 개선을 위한 적절한 접근입니다. 동적 배경 로직(isActive 조건)도 잘 보존되었습니다.

다만, inner wrapper에 px-4 패딩이 추가되어 기존 w-110에서 w-full로 변경되면서 시각적 여백이 달라질 수 있으니, 다양한 화면 크기에서 로그인 폼의 레이아웃이 의도대로 표시되는지 확인해 주세요.

src/app/password/find/result/page.tsx (2)

16-16: 레이아웃 개선이 적용되었습니다.

main 컨테이너에 h-screen, min-h-dvh, w-full을 함께 적용하여 전체 뷰포트 크기를 확보하고 있습니다. h-screenmin-h-dvh의 조합은 다양한 브라우저/디바이스 환경에서 일관된 높이를 보장하기 위한 것으로 보입니다.


17-17: LGTM! Inner wrapper의 너비 제약이 제거되었습니다.

w-110 제약을 w-full로 변경하고 h-full을 제거하여 더 유연한 레이아웃을 구현하고 있습니다. 컨텐츠가 자연스러운 높이를 가지도록 하는 의도로 보입니다.

src/components/onboarding/UploadButton.tsx (2)

23-36: LGTM! 접근성과 키보드 이벤트 처리가 잘 구현되어 있습니다.

Escape 키 핸들링과 모달 포커스 관리가 적절합니다. 이벤트 리스너 cleanup도 올바르게 처리되어 있습니다.


56-113: 모달 구조와 접근성 속성이 잘 적용되었습니다.

role="dialog", aria-modal, aria-labelledby 등 접근성 속성이 적절히 구현되어 있습니다. 향후 개선 사항으로 포커스 트랩(focus trap)을 추가하면 키보드 사용자 경험이 더 좋아질 수 있습니다.

src/components/onboarding/OnboardingProfile.tsx (2)

15-24: 컴포넌트 구조와 상태 관리가 잘 되어 있습니다.

커스텀 훅(useProfileImageUpload)을 활용한 관심사 분리와 상태 관리가 적절합니다.


94-101: and

src/app/signup/agree/page.tsx (1)

12-15: 이 코멘트는 적용되지 않습니다. 프로젝트의 tsconfig.json에서 "jsx": "react-jsx"를 사용하고 있으므로, React 네임스페이스 타입이 자동으로 스코프에 포함됩니다. 따라서 React.ChangeEvent<HTMLInputElement> 사용 시 명시적인 React import가 필요하지 않으며, 현재 코드는 올바릅니다.

Likely an incorrect or invalid review comment.

@idid10 idid10 merged commit 73d979b into develop Jan 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FEAT: 온보딩 UI

3 participants